﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO.Ports;
using System.Linq;
using System.Threading;

namespace XCSerialPort
{
   

    /// <summary>
    /// 串口管理器，提供对串口的基本操作功能。
    /// </summary>
    public class SerialPort_ARM_ISPManager : ISerialPortManager
    {
        private SerialPort _serialPort; // 串口对象

        public SerialPort_ARM_ISPManager() { }

        /// <summary>
        /// 使用配置初始化串口管理器。
        /// </summary>
        /// <param name="config">串口配置参数。</param>
        public SerialPort_ARM_ISPManager(ConfigType config)
        {
            _serialPort = new SerialPort
            {
                PortName = config.PortName, // 设置串口名称
                BaudRate = config.BaudRate, // 设置波特率
                DataBits = config.DataBits, // 设置数据位数
                StopBits = (System.IO.Ports.StopBits)config.StopBits, // 设置停止位
                Parity = (System.IO.Ports.Parity)config.Parity, // 设置奇偶校验
                ReadTimeout = 1000, // 设置读取超时时间
                WriteTimeout = 1000 // 设置写入超时时间
            };
        }

        /// <summary>
        /// 关闭串口并返回状态消息。
        /// </summary>
        /// <param name="msg">状态消息。</param>
        public void Close(out string msg)
        {
            try
            {
                if (_serialPort.IsOpen)
                {
                    _serialPort.Close(); // 关闭串口
                    msg = "串口关闭成功";
                }
                else
                {
                    msg = "串口已关闭";
                }
            }
            catch (Exception ex)
            {
                msg = "关闭串口失败: " + ex.Message; // 返回错误信息
            }
        }

        /// <summary>
        /// 获取所有可用串口的列表。
        /// </summary>
        /// <returns>串口名称的列表。</returns>
        public string[] GetSerialPorts(out string msg)
        {
            try
            {
                var ports = SerialPort.GetPortNames(); // 获取可用串口名称
                msg = "成功获取可用串口"; // 设置成功消息
                return ports; // 返回串口列表
            }
            catch (Exception ex)
            {
                msg = $"获取串口失败: {ex.Message}"; // 设置错误消息
                return Array.Empty<string>(); // 返回空数组
            }
        }

        /// <summary>
        /// 打开串口并返回状态消息。
        /// </summary>
        /// <param name="msg">状态消息。</param>
        /// <returns>成功打开串口则返回 true，否则返回 false。</returns>
        public bool Open(out string msg)
        {
            try
            {
                if (!_serialPort.IsOpen)
                {
                    _serialPort.Open(); // 打开串口
                }
                msg = "串口打开成功";
                return true;
            }
            catch (Exception ex)
            {
                msg = "打开串口失败: " + ex.Message; // 返回错误信息
                return false;
            }
        }


        /// <summary>
        /// 从指定的寄存器地址读取多个数据值（以 ulong 为单位）。
        /// </summary>
        /// <param name="addr">寄存器地址（4字节对齐）。</param>
        /// <param name="msg">输出参数，返回状态消息。</param>
        /// <param name="length">要读取的值的数量，默认为 1。</param>
        /// <param name="timeoutMilliseconds">读取超时的时间（毫秒），默认为 150 毫秒。</param>
        /// <returns>返回读取到的值列表。</returns>
        public List<ulong> ReadRegister(ulong addr, out string msg, int length = 1, int timeoutMilliseconds = 150)
        {
            List<ulong> values = new List<ulong>(new ulong[length]); // 创建一个列表以存储读取值
            msg = "Success"; // 默认成功消息

           

            try
            {
                for (int i = 0; i < length; i++)
                {
                    uint data = 0; // 用于存储从寄存器读取的数据
                                   // 将 int 转换为 ulong，确保地址计算无歧义
                    ulong targetAddr = addr + (ulong)(i);
                    byte response = ReadRegDataARM_New((uint)targetAddr, ref data, _serialPort, timeoutMilliseconds);
                   
                    // 检查响应
                    if (response == 0xFF)
                    {
                        msg = $"Failed: 地址 0x{targetAddr:X8}"; // 更新失败消息
                        return values; // 返回空的值列表
                    }

                    // 将读取的数据转换为 ulong 并存储在列表中
                    values[i] = data;
                    // 更新状态消息以包含每个成功读取的值
                    msg += $"  R: 0x{targetAddr:X8}, 0x{data:X2}"; 
                }
            }
            catch (Exception ex)
            {
                msg = "Failed: " + ex.Message; // 捕获异常并更新消息
            }

            return values; // 返回读取到的值列表
        }

        public List<ulong> ReadRegister1(ulong addr, out string msg, int length = 1, int timeoutMilliseconds = 150)
        {
            List<ulong> values = new List<ulong>(new ulong[length]); // 创建一个列表以存储读取值
            msg = "Success"; // 默认成功消息

            try
            {
                uint data = 0; // 用于存储从寄存器读取的数据
                               // 将 int 转换为 ulong，确保地址计算无歧义
                ulong targetAddr = addr ;
                byte response = ReadRegDataARM_New1((uint)targetAddr, ref data, _serialPort, 500);

                // 检查响应
                if (response == 0xFF)
                {
                    msg = $"Failed: 地址 0x{targetAddr:X8}"; // 更新失败消息
                    return values; // 返回空的值列表
                }

                // 将读取的数据转换为 ulong 并存储在列表中
                values[0] =data;
                // 更新状态消息以包含每个成功读取的值
                msg += $"  R: 0x{targetAddr:X8}, 0x{data:X}";
            }
            catch (Exception ex)
            {
                msg = "Failed: " + ex.Message; // 捕获异常并更新消息
            }

            return values; // 返回读取到的值列表
        }


        /// <summary>
        /// 从指定的寄存器地址读取单个数据值。
        /// </summary>
        /// <param name="ADDR">寄存器地址。</param>
        /// <param name="DATA">输出参数，返回读取到的数据。</param>
        /// <param name="sPort">串口对象。</param>
        /// <param name="timeoutMilliseconds">超时设置。</param>
        /// <returns>返回状态字节，成功返回0x31，失败返回0xFF。</returns>
        public static byte ReadRegDataARM_New(uint ADDR, ref uint DATA, SerialPort sPort, int timeoutMilliseconds = 150)
        {
            byte[] txbuf = new byte[5];
            byte[] rxbuf = new byte[5]; // 假设响应包括1个字节的状态和4个字节的数据  

            // 构造发送的字节数组  
            txbuf[0] = 0x31;
            txbuf[1] = (byte)((ADDR >> 0) & 0xff);
            txbuf[2] = (byte)((ADDR >> 8) & 0xff);
            txbuf[3] = (byte)((ADDR >> 16) & 0xff);
            txbuf[4] = (byte)((ADDR >> 24) & 0xff);
            var byteIndex = (int)(ADDR % 4); // 注意这里需要转换为int，因为数组索引是int  

            // 发送数据  
            sPort.Write(txbuf, 0, txbuf.Length);

            // 等待并读取响应，使用轮询  
            Stopwatch stopwatch = Stopwatch.StartNew();
            while (stopwatch.ElapsedMilliseconds < timeoutMilliseconds)
            {
                Thread.Sleep(3);
                int bytesRead = sPort.Read(rxbuf, 0, rxbuf.Length);
                if (bytesRead == 1 && rxbuf[0] == 0x31)
                {
                    sPort.Read(rxbuf, 1, 4);
                    // 解析数据
                    DATA = rxbuf[byteIndex + 1];
                    return 0x0;
                }
                else if (bytesRead == 5 && rxbuf[0] == 0x31)
                {
                    DATA = rxbuf[byteIndex + 1];
                    return 0x0;
                }
                else
                {
                    break;
                }

            }

            // 超时处理
            DATA = 0xffffffff; // 设置一个失败值
            return 0xFF; // 返回一个错误代码
        }



        /// <summary>
        /// 从指定的寄存器地址读取单个数据值。
        /// </summary>
        /// <param name="ADDR">寄存器地址。</param>
        /// <param name="DATA">输出参数，返回读取到的数据。</param>
        /// <param name="sPort">串口对象。</param>
        /// <param name="timeoutMilliseconds">超时设置。</param>
        /// <returns>返回状态字节，成功返回0x31，失败返回0xFF。</returns>
        public static byte ReadRegDataARM_New1(uint ADDR, ref uint DATA, SerialPort sPort, int timeoutMilliseconds = 150)
        {
            byte[] txbuf = new byte[5];
            byte[] rxbuf = new byte[5]; // 假设响应包括1个字节的状态和4个字节的数据  

            // 构造发送的字节数组  
            txbuf[0] = 0x11;
            txbuf[1] = (byte)((ADDR >> 0) & 0xff);
            txbuf[2] = (byte)((ADDR >> 8) & 0xff);
            txbuf[3] = (byte)((ADDR >> 16) & 0xff);
            txbuf[4] = (byte)((ADDR >> 24) & 0xff);
            var byteIndex = (int)(ADDR % 4); // 注意这里需要转换为int，因为数组索引是int  

            // 发送数据  
            sPort.Write(txbuf, 0, txbuf.Length);

            // 等待并读取响应，使用轮询  
            Stopwatch stopwatch = Stopwatch.StartNew();
            while (stopwatch.ElapsedMilliseconds < timeoutMilliseconds)
            {
                Thread.Sleep(3);
                int bytesRead = sPort.Read(rxbuf, 0, rxbuf.Length);
                if (bytesRead == 1 && rxbuf[0] == 0x31)
                {
                    sPort.Read(rxbuf, 1, 4);
                    // 解析数据
                    DATA = rxbuf[byteIndex + 1];
                    return 0x0;
                }
                else if (bytesRead == 5 && rxbuf[0] == 0x11)
                {
                    //DATA = rxbuf[byteIndex + 1];
                    DATA = (uint)(rxbuf[ 1] | (rxbuf[ 2] << 8) | (rxbuf[ 3] << 16) | (rxbuf[4] << 24));
                    return 0x0;
                }
                else
                {
                    break;
                }

            }

            // 超时处理
            DATA = 0xffffffff; // 设置一个失败值
            return 0xFF; // 返回一个错误代码
        }


        /// <summary>
        /// 向指定地址的寄存器写入值。
        /// </summary>
        /// <param name="addr">寄存器地址。</param>
        /// <param name="value">要写入的值列表。</param>
        /// <param name="msg">状态消息。</param>
        /// <param name="length">要写入的值的长度。</param>
        public void WriteRegister(ulong addr, List<ulong> value, out string msg, int length = 1, int timeoutMilliseconds = 150)
        {
            msg = "Success"; // 默认成功消息

            try
            {
                // 遍历要写入的值并逐一调用发送函数
                for (int i = 0; i < length; i++)
                {
                    // 将 int 转换为 ulong，以避免类型不匹配
                    ulong targetAddr = addr + (ulong)(i * 4); // 确保 addr 是 ulong 类型
                                                              // 调用发送数据函数，写入单个值
                    byte response = SendRegDataARM_New((uint)targetAddr, (uint)value[i], _serialPort);
                   
                    // 检查响应值，确保写入成功
                    if (response != 0x21) // 假设0x21是成功的状态字节
                    {
                        msg = $"Failed: 地址 0x{addr:X8} 值 {value[i]} 失败"; // 更新失败消息
                        return; // 退出方法
                    }
                    // 更新消息以包含写入的地址和值
                    msg += $"  W: 0x{targetAddr:X8}, 0x{value[i]:X2}"; 

                }
            }
            catch (Exception ex)
            {
                msg = "Failed: " + ex.Message; // 返回错误信息
            }
        }

        /// <summary>
        /// 向指定地址的寄存器发送数据。
        /// </summary>
        /// <param name="ADDR">寄存器地址。</param>
        /// <param name="DATA">要写入的数据。</param>
        /// <param name="sPort">串口对象。</param>
        /// <returns>返回状态字节，成功返回0x21，失败返回0xFF。</returns>
        public static byte SendRegDataARM_New(uint ADDR, uint DATA, SerialPort sPort)
        {
            byte[] sendbuf = new byte[9];
            byte[] responseBuf = new byte[8];
            byte responseValue = 0x00;
            var byteIndex = (int)(ADDR % 4);

            // 构造发送的字节数组
            sendbuf[0] = 0x21; // 命令头
            sendbuf[1] = (byte)(ADDR & 0xFF); // 地址低字节
            sendbuf[2] = (byte)((ADDR >> 8) & 0xFF); // 地址中低字节
            sendbuf[3] = (byte)((ADDR >> 16) & 0xFF); // 地址中高字节
            sendbuf[4] = (byte)((ADDR >> 24) & 0xFF); // 地址高字节
            sendbuf[5 + byteIndex] = (byte)(DATA & 0xFF); // 写入的数据

            // 发送数据
            sPort.Write(sendbuf, 0, sendbuf.Length);
            Thread.Sleep(5); // 短暂延迟以等待响应

            // 读取响应
            int bytesRead = sPort.Read(responseBuf, 0, responseBuf.Length);
            if (bytesRead > 0)
            {
                responseValue = responseBuf[0]; // 获取响应状态
                if (responseValue != 0x21) // 检查响应是否成功
                {
                    return 0xff; // 返回失败状态
                }
            }

            return responseValue; // 返回成功状态
        }


        /// <summary>
        /// 构建读取命令以发送到设备。
        /// </summary>
        /// <param name="addr">寄存器地址。</param>
        /// <param name="length">读取的长度。</param>
        /// <returns>构建的字节命令。</returns>
        private byte[] BuildReadCommand(ulong addr, int length)
        {
            byte[] command = new byte[3]; // 假设命令长度为3
            command[0] = 0x01; // 示例命令头
            command[1] = (byte)(addr & 0xFF); // 地址低位
            command[2] = (byte)(length); // 数据长度
            return command; // 返回构建的命令
        }

        /// <summary>
        /// 构建写入命令以发送到设备。
        /// </summary>
        /// <param name="addr">寄存器地址。</param>
        /// <param name="value">要写入的值列表。</param>
        /// <returns>构建的字节命令。</returns>
        private byte[] BuildWriteCommand(ulong addr, List<ulong> value)
        {
            List<byte> commandList = new List<byte> { 0x02, (byte)(addr & 0xFF), (byte)value.Count }; // 示例命令头和地址

            foreach (var val in value)
            {
                commandList.AddRange(BitConverter.GetBytes(val)); // 添加要写入的值
            }

            return commandList.ToArray(); // 返回构建的命令
        }

        public bool IsOpen()
        {
            return _serialPort?.IsOpen ?? false;
        }
    }
}
